/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using Lucene.Net.Util;

namespace Lucene.Net.Index
{
    
    /// <summary> Allows you to iterate over the <see cref="TermPositions" /> for multiple <see cref="Term" />s as
    /// a single <see cref="TermPositions" />.
    /// 
    /// </summary>
    public class MultipleTermPositions : TermPositions
    {
        private sealed class TermPositionsQueue : PriorityQueue<TermPositions>
        {
            internal TermPositionsQueue(LinkedList<TermPositions> termPositions)
            {
                Initialize(termPositions.Count);
                
                foreach(TermPositions tp in termPositions)
                    if (tp.Next())
                        Add(tp);
            }
            
            internal TermPositions Peek()
            {
                return Top();
            }
            
            public override bool LessThan(TermPositions a, TermPositions b)
            {
                return a.Doc < b.Doc;
            }
        }
        
        private sealed class IntQueue
        {
            public IntQueue()
            {
                InitBlock();
            }
            private void  InitBlock()
            {
                _array = new int[_arraySize];
            }
            private int _arraySize = 16;
            private int _index = 0;
            private int _lastIndex = 0;
            private int[] _array;
            
            internal void  add(int i)
            {
                if (_lastIndex == _arraySize)
                    growArray();
                
                _array[_lastIndex++] = i;
            }
            
            internal int next()
            {
                return _array[_index++];
            }
            
            internal void  sort()
            {
                System.Array.Sort(_array, _index, _lastIndex - _index);
            }
            
            internal void  clear()
            {
                _index = 0;
                _lastIndex = 0;
            }
            
            internal int size()
            {
                return (_lastIndex - _index);
            }
            
            private void  growArray()
            {
                int[] newArray = new int[_arraySize * 2];
                Array.Copy(_array, 0, newArray, 0, _arraySize);
                _array = newArray;
                _arraySize *= 2;
            }
        }
        
        private int _doc;
        private int _freq;
        private TermPositionsQueue _termPositionsQueue;
        private IntQueue _posList;

        private bool isDisposed;
        /// <summary> Creates a new <c>MultipleTermPositions</c> instance.
        /// 
        /// </summary>
        /// <exception cref="System.IO.IOException">
        /// </exception>
        public MultipleTermPositions(IndexReader indexReader, Term[] terms)
        {
            var termPositions = new System.Collections.Generic.LinkedList<TermPositions>();
            
            for (int i = 0; i < terms.Length; i++)
                termPositions.AddLast(indexReader.TermPositions(terms[i]));
            
            _termPositionsQueue = new TermPositionsQueue(termPositions);
            _posList = new IntQueue();
        }
        
        public bool Next()
        {
            if (_termPositionsQueue.Size() == 0)
                return false;
            
            _posList.clear();
            _doc = _termPositionsQueue.Peek().Doc;
            
            TermPositions tp;
            do 
            {
                tp = _termPositionsQueue.Peek();
                
                for (int i = 0; i < tp.Freq; i++)
                    _posList.add(tp.NextPosition());
                
                if (tp.Next())
                    _termPositionsQueue.UpdateTop();
                else
                {
                    _termPositionsQueue.Pop();
                    tp.Close();
                }
            }
            while (_termPositionsQueue.Size() > 0 && _termPositionsQueue.Peek().Doc == _doc);
            
            _posList.sort();
            _freq = _posList.size();
            
            return true;
        }
        
        public int NextPosition()
        {
            return _posList.next();
        }
        
        public bool SkipTo(int target)
        {
            while (_termPositionsQueue.Peek() != null && target > _termPositionsQueue.Peek().Doc)
            {
                TermPositions tp = _termPositionsQueue.Pop();
                if (tp.SkipTo(target))
                    _termPositionsQueue.Add(tp);
                else
                    tp.Close();
            }
            return Next();
        }

        public int Doc
        {
            get { return _doc; }
        }

        public int Freq
        {
            get { return _freq; }
        }

        [Obsolete("Use Dispose() instead")]
        public void  Close()
        {
            Dispose();
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (isDisposed) return;
            
            if (disposing)
            {
                while (_termPositionsQueue.Size() > 0)
                    _termPositionsQueue.Pop().Close();
            }

            isDisposed = true;
        }
        
        /// <summary> Not implemented.</summary>
        /// <throws>  UnsupportedOperationException </throws>
        public virtual void Seek(Term arg0)
        {
            throw new System.NotSupportedException();
        }
        
        /// <summary> Not implemented.</summary>
        /// <throws>  UnsupportedOperationException </throws>
        public virtual void Seek(TermEnum termEnum)
        {
            throw new System.NotSupportedException();
        }
        
        /// <summary> Not implemented.</summary>
        /// <throws>  UnsupportedOperationException </throws>
        public virtual int Read(int[] arg0, int[] arg1)
        {
            throw new System.NotSupportedException();
        }


        /// <summary> Not implemented.</summary>
        /// <throws>  UnsupportedOperationException </throws>
        public virtual int PayloadLength
        {
            get { throw new System.NotSupportedException(); }
        }

        /// <summary> Not implemented.</summary>
        /// <throws>  UnsupportedOperationException </throws>
        public virtual byte[] GetPayload(byte[] data, int offset)
        {
            throw new System.NotSupportedException();
        }

        /// <summary> </summary>
        /// <value> false </value>
// TODO: Remove warning after API has been finalized
        public virtual bool IsPayloadAvailable
        {
            get { return false; }
        }
    }
}